package com.auto.printer.common.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleCacheResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;

/**
 * @author wwj
 */
@Configuration
@EnableCaching
@Slf4j
public class CacheConfig extends CachingConfigurerSupport {

    @Autowired
    private RedisConnectionFactory factory;
    @Autowired
    private ObjectMapper objectMapper;
    @Value("${spring.redis.key-prefix:redisKeyPrefix}")
    private String redisKeyPrefix;

    /**
     * 自定义生成redis-key
     *
     * @return
     */
    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return (o, method, objects) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(o.getClass().getName()).append(".");
            sb.append(method.getName()).append(".");
            for (Object obj : objects) {
                sb.append(obj.toString());
            }
            log.debug("keyGenerator {}",sb.toString());
            return sb.toString();
        };
    }



    @Bean
    @Override
    public CacheResolver cacheResolver() {
        return new SimpleCacheResolver(cacheManager());
    }

    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        // 用于捕获从Cache中进行CRUD时的异常的回调处理器。
//        return new SimpleCacheErrorHandler();
        return new IgnoreExceptionCacheErrorHandler();
    }

    @Bean
    @Primary
    @Override
    public CacheManager cacheManager() {
        ObjectMapper copy = objectMapper.copy();
        copy.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        copy.configure(MapperFeature.USE_ANNOTATIONS, false);
        copy.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        copy.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 此项必须配置，否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        //已过时
        //copy.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        //新api
        copy.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        copy.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //解决localDateTime的序列化问题
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(copy);


        RedisCacheConfiguration cacheConfiguration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .disableCachingNullValues()
                        //设置缓存时间为1天失效
                        .entryTtl(Duration.ofDays(1))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
                .prefixCacheNameWith(redisKeyPrefix);
        return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
    }


    @Bean
    public CacheManager cacheManager_15S() {
        ObjectMapper copy = objectMapper.copy();
        copy.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        copy.configure(MapperFeature.USE_ANNOTATIONS, false);
        copy.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        copy.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 此项必须配置，否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        //已过时
        //copy.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        //新api
        copy.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        copy.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //解决localDateTime的序列化问题
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(copy);


        RedisCacheConfiguration cacheConfiguration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .disableCachingNullValues()
                        //设置缓存时间
                        .entryTtl(Duration.ofSeconds(15))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
                        .prefixCacheNameWith(redisKeyPrefix);
        return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
    }

    @Bean
    public CacheManager cacheManager_60S() {
        ObjectMapper copy = objectMapper.copy();
        copy.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        copy.configure(MapperFeature.USE_ANNOTATIONS, false);
        copy.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        copy.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 此项必须配置，否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        //已过时
        //copy.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        //新api
        copy.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        copy.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //解决localDateTime的序列化问题
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(copy);


        RedisCacheConfiguration cacheConfiguration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .disableCachingNullValues()
                        //设置缓存时间
                        .entryTtl(Duration.ofSeconds(60))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
                        .prefixCacheNameWith(redisKeyPrefix);
        return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
    }

    @Bean
    public CacheManager cacheManager_5min() {
        ObjectMapper copy = objectMapper.copy();
        copy.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        copy.configure(MapperFeature.USE_ANNOTATIONS, false);
        copy.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        copy.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 此项必须配置，否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        //已过时
        //copy.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        //新api
        copy.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        copy.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //解决localDateTime的序列化问题
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(copy);


        RedisCacheConfiguration cacheConfiguration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .disableCachingNullValues()
                        //设置缓存时间
                        .entryTtl(Duration.ofSeconds(300))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
                        .prefixCacheNameWith(redisKeyPrefix);
        return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
    }
} 